#include <memory>
#include <ql_oe.h>

#include "sys_var.hpp"
#include "gps.hpp"

gpsCtrl * gpsCtrl :: gpsDevice = nullptr;

void gps_loc_rx_ind_msg_cb(loc_client_handle_type  h_loc,  E_QL_LOC_NFY_MSG_ID_T   e_msg_id,  void *pv_data, void *contextPtr )
{
    	gpsCtrl * ptr = ( gpsCtrl *)contextPtr;
	if( ptr == nullptr ) return;
    	switch(e_msg_id) {
        case E_QL_LOC_NFY_MSG_ID_STATUS_INFO:  // 处理GPS状态
			ptr->ProcessStatus( (E_QL_LOC_STATUS_VALUE_T*)pv_data );
            break;
        case E_QL_LOC_NFY_MSG_ID_LOCATION_INFO:
        	ptr->ProcessLocationInfo( (QL_LOC_LOCATION_INFO_T *)pv_data );
            break;
        case E_QL_LOC_NFY_MSG_ID_SV_INFO:
			ptr->ProcessSVInfo( (QL_LOC_SV_STATUS_T*)pv_data );
            break;
        case E_QL_LOC_NFY_MSG_ID_NMEA_INFO:
			ptr->ProcessNMEAInfo( (QL_LOC_NMEA_INFO_T  *)pv_data );
			break;
        case E_QL_LOC_NFY_MSG_ID_CAPABILITIES_INFO:
			ptr->ProcessCapabilities((E_QL_LOC_CAPABILITIES_T*)(pv_data));
            break;
        case E_QL_LOC_NFY_MSG_ID_AGPS_STATUS:
			ptr->ProcessAGPSStatus( (QL_LOC_AGPS_STATUS_T*)(pv_data));
            break;
        case E_QL_LOC_NFY_MSG_ID_NI_NOTIFICATION:
			ptr->ProcessNotifaction( (QL_LOC_NI_NOTIFICATION_INTO_T*)pv_data );
            break;
        case E_QL_LOC_NFY_MSG_ID_XTRA_REPORT_SERVER:
			ptr->ProcessXTRAReport( (QL_LOC_XTRA_REPORT_SERVER_INTO_T*)pv_data );
            break;
    	}
}

gpsCtrl::gpsCtrl()
{
	bitmask = 0x0ff;
	int ret = 0;
    	ret = QL_LOC_Client_Init(&h_loc);
	if( E_QL_OK == ret )
    		ret = QL_LOC_AddRxIndMsgHandler( gps_loc_rx_ind_msg_cb , this );
	else{
		std::cout << "Can not initialize GPS for initialize context fail." << std::endl;
		abort();
	}

	init_bitmask();

	init_position_mode();

	if( E_QL_OK == ret )
    	ret = QL_LOC_Start_Navigation(h_loc);
	else{
		std::cout << "Can not initialize GPS for start GPS fail." << std::endl;
		abort();
	}
}

gpsCtrl :: ~gpsCtrl()
{
	if( m_is_running )
		QL_LOC_Stop_Navigation(h_loc);

    	QL_LOC_Client_Deinit(h_loc);
}

void gpsCtrl :: init_bitmask()
{
	// NiNfy
	// AGPS , 辅助定位；可以通过基站定位，网络定位进行辅助操作
	// XTRA
	// UTC
	// CAP ，
	// NMEA , 定位nmea原属数据
	// SV , 卫星信息
	// Status , 状态信息
	// 位置信息 ，
	// bitmask(NiNfy|AGPS|XTRA|UTC|CAP|NMEA|SV|Status|Location)
	std::shared_ptr< CSysVar > ptvar = GetOrCreateSysVar();
	bitmask = 1;
	if( ptvar ){
		bool data = false;
		ptvar->GetValue( "/gps/NiNfy" , data );
		if( data ){
			bitmask |= ( 1 << 8 );
		}
		ptvar->GetValue( "/gps/agps/enable" , data );
		if( data ){
			bitmask |= ( 1 << 7 );
		}
		ptvar->GetValue( "/gps/XTRA" , data );
		if( data ){
			bitmask |= ( 1 << 6 );
		}
		ptvar->GetValue( "/gps/UTC" , data );
		if( data ){
			bitmask |= ( 1 << 5 );
		}
		ptvar->GetValue( "/gps/CAP" , data );
		if( data ){
			bitmask |= ( 1 << 4 );
		}
		ptvar->GetValue( "/gps/NMEA" , data );
		if( data ){
			bitmask |= ( 1 << 3 );
		}
		ptvar->GetValue( "/gps/sv" , data );
		if( data ){
			bitmask |= ( 1 << 2 );
		}
		ptvar->GetValue( "/gps/status" , data );
		if( data ){
			bitmask |= 2;
		}
    	int ret = QL_LOC_Set_Indications(h_loc, bitmask);
		if( E_QL_OK != ret ){ // 设置bitmask出错
			std::cout << "Can not initialize GPS for set indications fail." << std::endl;
			abort();
		}
	}else{
		std::cout << "Can not initialize GPS for System variable is null" << std::endl;
		abort();
	}
}

void gpsCtrl :: init_position_mode()
{
	std::shared_ptr<CSysVar> ptvar = GetOrCreateSysVar();
	if( ptvar ){
		std::string d2;
		bool d3 = false;
		ptvar->GetValue( "/gps/agps/enable" , d3 );
		if( d3 ){
			ptvar->GetValue( "/gps/agps/mtd" , d2 );
			if( d2 == "MSBase" ){
				t_mode.mode = E_QL_LOC_POS_MODE_MS_BASED;
			}else if( d2 == "MSAssis" ){
				t_mode.mode = E_QL_LOC_POS_MODE_MS_ASSISTED;
			}else{
				t_mode.mode = E_QL_LOC_POS_MODE_STANDALONE;
			}
		}else{
			t_mode.mode = E_QL_LOC_POS_MODE_STANDALONE;
		}

	    	t_mode.recurrence  = E_QL_LOC_POS_RECURRENCE_PERIODIC;
		int data;
	   	 ptvar->GetValue( "/gps/updateInterval" , data );    // report nmea frequency 1Hz
		t_mode.min_interval = data;
	    	ptvar->GetValue( "/gps/accuracy" , data );          // 精度
		t_mode.preferred_accuracy = data;
	    	t_mode.preferred_time       = 90;                   // 90s

	    	int ret = QL_LOC_Set_Position_Mode(h_loc, &t_mode);
		if( E_QL_OK != ret ){
			std::cout << "Can not initialize GPS for set init position mode fail." << std::endl;
			abort();
		}
	}else{
		std::cout << "Can not initialize GPS for System variable is null" << std::endl;
		abort();
	}
}

void gpsCtrl :: ProcessStatus( E_QL_LOC_STATUS_VALUE_T * status )
{

}


void gpsCtrl :: Start( bool sw )
{
	if( sw ){
		if( m_is_running ) return;
		QL_LOC_Start_Navigation(h_loc);
	}else{
		QL_LOC_Stop_Navigation( h_loc );
		m_is_running = false;
	}
}

void gpsCtrl :: Start( bool sw , std::function< void ( bool )> fun )
{
	if( fun ){
		cb_on_navigate = fun;
	}

	if( sw ){
		if( m_is_running ) return;
		QL_LOC_Start_Navigation(h_loc);
	}else{
		QL_LOC_Stop_Navigation( h_loc );
		m_is_running = false;
	}
}


void gpsCtrl :: ProcessLocationInfo( QL_LOC_LOCATION_INFO_T* data )
{
	if( data == nullptr ) return;

	memcpy( &t_loc_info , data , sizeof( QL_LOC_LOCATION_INFO_T ));
	/*printf("**** flag=0x%X, Latitude = %f, Longitude=%f, altitude=%f,accuracy = %f ****\n",
	                        data->flags,
	                        data->latitude,
	                        data->longitude,
							data->altitude,
	                        data->accuracy);

	if( cb_on_data ){
		cb_on_data( data->latitude , data->longitude , data->altitude , data->accuracy );
	}

	if( data->speed > 0.01 ){ // 如果移动速度大于0.01m/s, 则认为设备在移动。否则不认为设备在移动
		cb_on_speed( data->speed , data->bearing );
	}
	*/
}

void gpsCtrl :: ProcessSVInfo( QL_LOC_SV_STATUS_T *data )
{
	if( data == nullptr ) return;

	memcpy( &t_sv_info , data , sizeof( QL_LOC_SV_STATUS_T ));
	/*
	if( cb_on_sv ){
		cb_on_sv( data->num_svs );
	}*/
}

void gpsCtrl ::  ProcessNMEAInfo( QL_LOC_NMEA_INFO_T  * data )
{
	if( data == nullptr ) return;
	/*
	printf("NMEA info: timestamp=%lld, length=%d, nmea=%s\n",
	                    data->timestamp, data->length, data->nmea);
	*/
}

void gpsCtrl :: ProcessCapabilities( E_QL_LOC_CAPABILITIES_T* data )
{

}
void gpsCtrl :: ProcessAGPSStatus(  QL_LOC_AGPS_STATUS_T* data )
{
	if( data == nullptr ) return;

	memcpy( &m_agps_status , data , sizeof( QL_LOC_AGPS_STATUS_T ));
}

void gpsCtrl :: ProcessNotifaction(  QL_LOC_NI_NOTIFICATION_INTO_T*  data )
{

}

void gpsCtrl :: ProcessXTRAReport(  QL_LOC_XTRA_REPORT_SERVER_INTO_T* data )
{

}

void gpsCtrl :: Location( double & latitude , double& longitude , double& altitude , double & accuracy )
{
	if( m_is_running ){
		latitude = t_loc_info.latitude;
		longitude = t_loc_info.longitude;
		altitude = t_loc_info.altitude;
		accuracy = t_loc_info.accuracy;
	}else{
		latitude = -1;
		longitude = -1;
		altitude = -1;
		accuracy = -1;
	}
}

void gpsCtrl :: Speed( float & speed , float & heading )
{
	if( m_is_running ){
		speed = t_loc_info.speed;
		heading = t_loc_info.bearing;
	}else{
		speed = -1;
		heading = -1;
	}
}

int gpsCtrl :: SvCount()
{
	if( m_is_running ){
		return t_sv_info.num_svs;
	}

	return -1;
}
gpsCtrl * CreateGPS()
{
	gpsCtrl * ret = nullptr;
	std::shared_ptr< CSysVar > ptvar = GetOrCreateSysVar();
	bool enable;
	if( ptvar ){
		ptvar->GetValue( "/gps/enable" , enable );
	}
	if( enable == false ){
		if( gpsCtrl :: gpsDevice ){
			delete gpsCtrl :: gpsDevice;
		}

		gpsCtrl :: gpsDevice = nullptr;
		return nullptr;
	}

	if( gpsCtrl :: gpsDevice != nullptr ){
		ret = gpsCtrl :: gpsDevice;
	}
	else{
		ret = new gpsCtrl();
		gpsCtrl::gpsDevice = ret;
	}

	return ret;
}
